package edu.ncsu.helloandroid;

import java.util.ArrayList;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnKeyListener;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

public class FGCourse extends FGCActivity {
	
	private static final int HINT_TIMER_BASE_DELAY = 5000;
	private static final int DELAY_UNIT = 2000;

	private View hole = null;
	private TextView score_txt = null;
	private TextView hole_txt = null;
	private ImageView prev_arrow = null;
	private ImageView next_arrow = null;
	private ImageView frisbee = null;
	private ImageView player = null;
	private ImageView adviceIcon = null;
	private ImageView checkmark = null;
	private Runnable showHintRunnable = null;
	private Handler messageHandler = new Handler();
	private int visibleHoleNumber = 1;
//	private int currPlayingHoleNumber = 1;
//	private int throwNumber = 1;
//	private int playerPos = 1;
	private FGCGameState state = null;
	
	private static final String SHOW_ADVICE_DATA = "showAdvice";
	private static final String HINT_TIMER_DELAY_DATA = "hintDelay";
	private static final String HINT_NO_COUNT = "hintNoCount";
	
	private static int hintTimerDelay = HINT_TIMER_BASE_DELAY;
	private static int hintNoCount = 0;
	
	private static boolean showAdviceNag = true;
	private boolean resumeWithAdviceShown = false;
	private AlertDialog hintDialog;
	

	private static final int SWIPE_MIN_DISTANCE = 120;
	private static final int SWIPE_MAX_OFF_PATH = 250;
	private static final int SWIPE_THRESHOLD_VELOCITY = 200;
	
	private GestureDetector gestureDetector;
	View.OnTouchListener gestureListener;

	
	private static void incrementHintDelay(){
		hintNoCount++;
		hintTimerDelay = HINT_TIMER_BASE_DELAY + ( hintNoCount * DELAY_UNIT);
	}
	
	private static void decrementHintDelay(){
		if(hintNoCount > 0){
			hintNoCount--;
		}
		hintTimerDelay = HINT_TIMER_BASE_DELAY + ( hintNoCount * DELAY_UNIT);
	}
	
	private static void resetHintDelay(){
		hintTimerDelay = HINT_TIMER_BASE_DELAY;
		hintNoCount = 0;
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		if(savedInstanceState != null){
			showAdviceNag = savedInstanceState.getBoolean(SHOW_ADVICE_DATA, true);
			hintTimerDelay = savedInstanceState.getInt(HINT_TIMER_DELAY_DATA,HINT_TIMER_BASE_DELAY);
			hintNoCount = savedInstanceState.getInt(HINT_NO_COUNT,0);
		}
		state = FGCGameState.getGameState(getTaskId());
		visibleHoleNumber = state.getCurrentHole();
		
		createContentView();
		
		setResult(RESULT_OK);
		
        gestureDetector = new GestureDetector(new MyGestureDetector(this));
        gestureListener = new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
                return false;
            }
        };
	}


	private void createContentView() {
		
		hole = View.inflate(this, R.layout.hole, null);

		score_txt = ((TextView)hole.findViewById(R.id.Score_Value));
		hole_txt = ((TextView)hole.findViewById(R.id.Hole_Value));
		prev_arrow = ((ImageView)hole.findViewById(R.id.previousHoleButton));
		next_arrow = ((ImageView)hole.findViewById(R.id.nextHoleButton));
		frisbee = ((ImageView)hole.findViewById(R.id.frisbeeIndicator));
		player = ((ImageView)hole.findViewById(R.id.playerIndicator));
		adviceIcon = ((ImageView)hole.findViewById(R.id.adviceIconButton));
		checkmark = ((ImageView)hole.findViewById(R.id.holeCompleteIcon));
		adviceIcon.setAlpha(220);
		ArrayList<View> touchables = new ArrayList<View>();
		touchables.add(prev_arrow);
		touchables.add(next_arrow);
		hole.addTouchables(touchables);
		
		Resources res = getResources();
		hole_txt.setText(Integer.toString(visibleHoleNumber)+ " (" + Integer.toString(res.getIntArray(R.array.hole_lengths)[visibleHoleNumber-1])+"ft)");
		
		prev_arrow.setAlpha(100);
		prev_arrow.setOnTouchListener(new OnTouchListener() {
			private Rect rect = new Rect();
			public boolean onTouch(View v, MotionEvent event) {	
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
				if(event.getAction() == MotionEvent.ACTION_DOWN){
					prev_arrow.setAlpha(255);
				}
				if(event.getAction() == MotionEvent.ACTION_UP){
					prev_arrow.getHitRect(rect);
					prev_arrow.setAlpha(100);
					if(rect.contains((int)event.getRawX(), (int)event.getRawY())){
						previousHole();
					}
					
				}
				return false;
			}
		});
		next_arrow.setAlpha(100);
		next_arrow.setOnTouchListener(new OnTouchListener() {
			
			private Rect rect = new Rect();
			public boolean onTouch(View v, MotionEvent event) {	
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
				if(event.getAction() == MotionEvent.ACTION_DOWN){
					next_arrow.setAlpha(255);
				}
				if(event.getAction() == MotionEvent.ACTION_UP){
					next_arrow.getHitRect(rect);
					next_arrow.setAlpha(100);
					if(rect.contains((int)event.getRawX(), (int)event.getRawY())){
						nextHole();
					}
					
				}
				return false;
			}
		});
		
		
		initializeHintNag();
		int[] left = res.getIntArray(R.array.left);
		int[] top = res.getIntArray(R.array.top);
		frisbee.setPadding(left[state.getThrowCount()], top[state.getThrowCount()], 0, 0);
		frisbee.setVisibility(View.VISIBLE);
		player.setPadding(left[state.getPlayerPos()], top[state.getPlayerPos()], 0, 0);
		player.setVisibility(View.VISIBLE);
		changeVisibleHole(visibleHoleNumber);
		score_txt.setText(Integer.toString(state.getScores()[visibleHoleNumber-1]));
		setContentView(hole);
	}

	private void initializeHintNag() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);

		builder.setMessage("Need a hint?")
	       .setCancelable(true)
	       .setOnKeyListener(new OnKeyListener() {
			
				public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
					incrementHintDelay();
					onKeyDown(keyCode, event);  //Call through to Activity keyDown listener
					return false;
				}
				
	       })
	       .setPositiveButton("Yes Please!", new DialogInterface.OnClickListener() {
	           public void onClick(DialogInterface dialog, int id) {
	        	   //Launch Advice activity
	        	   FGCourse.this.startActivityForResult(new Intent(FGCourse.this,FGCAdvice.class),CREATE_ADVICE_ACTIVITY);
	        	   decrementHintDelay();
	           }
	       })
	       .setNegativeButton("No Thanks.", new DialogInterface.OnClickListener() {
	           public void onClick(DialogInterface dialog, int id) {
	                dialog.cancel();
	                incrementHintDelay();
	           }
	       });
		
		hintDialog = builder.create();
		showHintRunnable = new Runnable() {
			
			public void run() {
				adviceIcon.setVisibility(View.VISIBLE);
			}
		
		};
	}
	
	/**
	 * Callback from Advice icon
	 * @param target
	 */
	public void showAdvice(View target){
		hintDialog.show();
		adviceIcon.setVisibility(View.INVISIBLE);
//		FGCourse.this.startActivityForResult(new Intent(FGCourse.this,FGCAdvice.class),CREATE_ADVICE_ACTIVITY);
	}

	/**
	 * Callback from Score text
	 * @param target
	 */
	public void showScore(View target){
		FGCourse.this.startActivityForResult(new Intent(FGCourse.this,FGCScorecard.class),CREATE_SCORECARD_ACTIVITY);
	}
	
	
	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState.putBoolean(SHOW_ADVICE_DATA, showAdviceNag);
		outState.putInt(HINT_NO_COUNT, hintNoCount);
		outState.putInt(HINT_TIMER_DELAY_DATA, hintTimerDelay);
		
		super.onSaveInstanceState(outState);
	}
	
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		stopAdviceNag();
		return super.onPrepareOptionsMenu(menu);
	}
	
	@Override
	public void onOptionsMenuClosed(Menu menu) {
		startAdviceNag();
		super.onOptionsMenuClosed(menu);
	}
	
	@Override
	protected void onResume() {
		if(resumeWithAdviceShown){
			adviceIcon.setVisibility(View.VISIBLE);
		} else {
			startAdviceNag();
		}
		resumeWithAdviceShown = false;
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		stopAdviceNag();
		super.onPause();
	}
	
	public void nextHole() {
		if(visibleHoleNumber < 18){
			visibleHoleNumber++;
			changeVisibleHole(visibleHoleNumber);
		}
	}
	
	public void previousHole() {
		if(visibleHoleNumber > 1){
			visibleHoleNumber--;
			changeVisibleHole(visibleHoleNumber);
		}
	}
	
	private void changeVisibleHole(int newHoleNumber) {
		Resources res = getResources();
		hole_txt.setText(Integer.toString(newHoleNumber)+ " (" + Integer.toString(res.getIntArray(R.array.hole_lengths)[newHoleNumber-1])+"ft)");
	    if (newHoleNumber == 1) {
	    	prev_arrow.setVisibility(View.INVISIBLE);
	    }
	    else {
			prev_arrow.setVisibility(View.VISIBLE);
		}
		if (newHoleNumber == FGCGameState.LAST_HOLE) {
			next_arrow.setVisibility(View.INVISIBLE);
		}
		else {
			next_arrow.setVisibility(View.VISIBLE);
		}
		if (newHoleNumber != state.getCurrentHole()) {
			frisbee.setVisibility(View.INVISIBLE);
			player.setVisibility(View.INVISIBLE);
		}
		else {
			frisbee.setVisibility(View.VISIBLE);
			player.setVisibility(View.VISIBLE);
		}
		hole.setBackgroundResource(getHoleImageId(newHoleNumber));
		
		stopAdviceNag();
		if(state.getCurrentHole() == newHoleNumber){
			startAdviceNag();
			frisbee.setVisibility(View.VISIBLE);
			player.setVisibility(View.VISIBLE);
		} else {
			frisbee.setVisibility(View.INVISIBLE);
			player.setVisibility(View.INVISIBLE);
		}
		
		if (state.isHoleComplete(newHoleNumber)) {
			checkmark.setVisibility(View.VISIBLE);
		}
		else {
			checkmark.setVisibility(View.INVISIBLE);
		}

		if(state.getCurrentHole() == newHoleNumber || state.getScores()[newHoleNumber - 1] > 0){
			score_txt.setText(Integer.toString(state.getScores()[newHoleNumber - 1]));
		} else {
			score_txt.setText("--");
		}
		
	}
	
	private int getHoleImageId(int holeNum) {
		switch (holeNum) {
			case 1: return R.drawable.sample_hole;
			case 2: return R.drawable.sample_hole_2;
			case 3: return R.drawable.sample_hole_3;
			case 4: return R.drawable.sample_hole_4;
			case 5: return R.drawable.sample_hole_5;
			case 6: return R.drawable.sample_hole_6;
			case 7: return R.drawable.sample_hole_7;
			case 8: return R.drawable.sample_hole_8;
			case 9: return R.drawable.sample_hole_9;
			case 10: return R.drawable.sample_hole_10;
			case 11: return R.drawable.sample_hole_11;
			case 12: return R.drawable.sample_hole_12;
			case 13: return R.drawable.sample_hole_13;
			case 14: return R.drawable.sample_hole_14;
			case 15: return R.drawable.sample_hole_15;
			case 16: return R.drawable.sample_hole_16;
			case 17: return R.drawable.sample_hole_17;
			case 18: return R.drawable.sample_hole_18;
			default: return R.drawable.sample_hole;
		}
	}
	
	public void startAdviceNag(){
		if(showAdviceNag){
			messageHandler.postDelayed(showHintRunnable, hintTimerDelay);
		}
	}
	
	public void stopAdviceNag(){
		messageHandler.removeCallbacks(showHintRunnable);
		adviceIcon.setVisibility(View.INVISIBLE);
	}
	
	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.fgcourse_options_menu, menu);
		updateAdviceItem(menu.findItem(R.id.fgc_AdviceItem));
				
		return super.onCreateOptionsMenu(menu);
	}

	private void updateAdviceItem(MenuItem menu) {
		if(showAdviceNag){
			menu.setTitle("No More Advice");
		} else {
			menu.setTitle("Provide Advice");
		}
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if(item.getItemId() == R.id.fgc_AdviceItem){
			showAdviceNag = !showAdviceNag;
			resetHintDelay();
			updateAdviceItem(item);
		}
		return super.onOptionsItemSelected(item);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
		case CREATE_ADVICE_ACTIVITY:
			resumeWithAdviceShown = true;  //We gave advice...
			break;

		default:
			break;
		}

		
	}

	
	@Override
	protected void onNewIntent(Intent intent) {
		setIntent(intent);
		super.onNewIntent(intent);
	}
	
	/**
	 * 
	 * @param number Position on current hole to place player/frisbee
	 * @return TRUE if throws can continue  (not at last spot)
	 */
	public void sendImageViewTo(final ImageView image, final int number, boolean animate,  boolean useDiscAnimation){
		Resources res = getResources();
		final int[] left = res.getIntArray(R.array.left);
		final int[] top = res.getIntArray(R.array.top);
		if (animate) {
			TranslateAnimation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, left[number] - left[number-1], Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, top[number] - top[number-1]);
			anim.setInterpolator(new LinearInterpolator());
			anim.setDuration(1500);
			if (useDiscAnimation) {
				anim.setAnimationListener(new DiscAnimationListener() {
					public void onAnimationEnd(Animation animation) {
						image.setPadding(left[number], top[number], 0, 0);
						super.onAnimationEnd(animation);
					}
				});
			}
			else {
				anim.setAnimationListener(new AnimationListener() {
					public void onAnimationEnd(Animation animation) {
						image.setPadding(left[number], top[number], 0, 0);
					}
					public void onAnimationRepeat(Animation animation) {}
					public void onAnimationStart(Animation animation) {}
				});
			}
			image.startAnimation(anim);
		}
		else {
			image.setPadding(left[number], top[number], 0, 0);
		}
//		return (number < top.length-1) || (number < left.length-1);
	}
	
	public boolean checkHoleComplete(int number) {
		Resources res = getResources();
		return (res.getIntArray(R.array.in_basket))[number] == 1;
	}
	
	public void completeHole() {
		state.completeCurrentHole();
		checkmark.setVisibility(View.VISIBLE);
		if (state.getCurrentHole() == FGCGameState.LAST_HOLE) {
			completeGame();
		}
	}
	
	public void completeGame() {
		state.setCurrentHole(Integer.MAX_VALUE);
		showScore(null);
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if(state.getCurrentHole() == visibleHoleNumber){
			if(!state.isHoleComplete(state.getCurrentHole())){
	
				switch (keyCode) {
				case KeyEvent.KEYCODE_T:  //throwing...
					if(frisbee.getAnimation() == null){
						sendImageViewTo(frisbee, state.getThrowCount()+1, true, true);
					}
					break; 
					
				case KeyEvent.KEYCODE_P: //player moving...
					if(player.getAnimation() == null){
						sendImageViewTo(player, state.playerMoved(), true, false);
					}
			    	break;
			    default:
			    	break;
				}
			
			} else {
				switch (keyCode) {
				case KeyEvent.KEYCODE_N:   //player and disc moving to next hole...
					state.setCurrentHole(state.getCurrentHole() + 1);
			    	sendImageViewTo(player, state.playerMoved(), false, false);
			    	sendImageViewTo(frisbee, state.discThrown(), false, false);
			    	nextHole();
			    	break;
				case KeyEvent.KEYCODE_P: //player moving...
					if(player.getAnimation() == null){
						sendImageViewTo(player, state.playerMoved(), true, false);
					}
			    	break;
			    default:
			    	break;
				}
			}
			hintDialog.dismiss();
			stopAdviceNag();
			startAdviceNag();
		}
		return super.onKeyDown(keyCode, event);
	}
	
    class MyGestureDetector extends SimpleOnGestureListener {
    	FGCourse fgCourse;
    	public MyGestureDetector(FGCourse course) {
    		fgCourse = course;
    	}
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            try {
                if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                    return false;
                if (Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
	                // right to left swipe
	                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE) {
	                	//next hole
	                	fgCourse.nextHole();
	                }  
	                else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE) {
	                	//previous hole
              			fgCourse.previousHole();
	                }
                }
            } catch (Exception e) {
                // nothing
            }
            return false;
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (gestureDetector.onTouchEvent(event))
	        return true;
	    else
	    	return false;
    }
    
    class DiscAnimationListener implements AnimationListener {
		public void onAnimationEnd(Animation animation) {

    		score_txt.setText(Integer.toString(++state.getScores()[state.getCurrentHole()-1]));
    		if (checkHoleComplete(state.discThrown())) {
    			completeHole();
    		}
		}
		public void onAnimationRepeat(Animation animation) {}
		public void onAnimationStart(Animation animation) {}
    }
	

}


